package com.onpositive.mediawiki.dataminer;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;

import org.eclipse.core.runtime.Platform;

import com.onpositive.data.IPropertyConstants;
import com.onpositive.dataminer.core.DefaultUniverse;
import com.onpositive.dataminer.core.ICanExtractImages;
import com.onpositive.dataminer.core.IShouldBeNotified;
import com.onpositive.dataminer.core.AuditManager.InspectionDeltaUnit;
import com.onpositive.dataminer.userstorage.UserValueStorage;
import com.onpositive.knowledge.model.ICanResolveWikiRefs;
import com.onpositive.knowledge.model.IHasVersions;
import com.onpositive.knowledge.model.IThing;
import com.onpositive.knowledge.model.IUniverse;
import com.onpositive.knowledge.model.values.DescribedValue;
import com.onpositive.knowledge.model.values.IValueTransformer;
import com.onpositive.mediawiki.propertyparser.ParsedDocument;
import com.onpositive.mediawiki.propertyparser.PropertyCharacteristic;
import com.onpositive.semantic.model.api.meta.DefaultMetaKeys;
import com.onpositive.semantic.model.api.property.IProperty;
import com.onpositive.semantic.model.api.property.PropertyAccess;
import com.onpositive.semantic.model.api.realm.IRealm;
import com.onpositive.semantic.model.api.realm.Realm;
import com.onpositive.units.AbstractValueParser;
import com.onpositive.units.ParsedProperty;
import com.onpositive.units.ParsedValue;
import com.onpositive.wikistorage.PictureExtractor;
import com.onpositive.wikistorage.XMLInterface;

import storage.v1.ActualityIndicationProperty;
import storage.v1.DefaultParserProvider;
import storage.v1.DefaultPictureProperty;
import storage.v1.DocumentIdProperty;
import storage.v1.ExternalDefaultPictureProperty;
import storage.v1.LowLevelStorage;
import storage.v1.ParentCategoriesProperty;
import storage.v1.ProperyDataExtractor;
import storage.v1.ReplacementRequest;
import storage.v1.WikiStorageProperty;

public class MediaWikiUniverse extends LowLevelStorage implements IUniverse,
		IShouldBeNotified,ICanExtractImages, ICanResolveWikiRefs, IHasVersions {
	
	

	/**
	 * 
	 */
	private static final long serialVersionUID = 848794800454676956L;
	private String pcategory;
	
	private static final String STORAGE_SUBBPATH = "categoryVersions/" ;
	private static final String STANDARD_STORAGE_FILENAME = "standardStorage.xml" ;
	private static final String PREVIOUS_VALUES_STORAGE_FILENAME = "previousStorage.xml" ;
	
	UserValueStorage standardStorage 	  = new UserValueStorage(this) ;
	UserValueStorage previousValueStorage = new UserValueStorage(this) ;
	{
		try {
			URL url = Platform.getInstanceLocation().getDataArea(STORAGE_SUBBPATH) ;
			File dirFile = new File( url.toURI() ) ;
			dirFile.mkdirs() ;
			File f1 = new File( dirFile, STANDARD_STORAGE_FILENAME ) ;
			File f2 = new File( dirFile, PREVIOUS_VALUES_STORAGE_FILENAME ) ;
			standardStorage.setFile(f1);
			previousValueStorage.setFile(f2) ;
		} catch (IOException e) {
			e.printStackTrace();
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
	}
	public class InspectionDeltaUnit {
		
		private final String id ;
		private final Object correct ;
		private final Object previous ;		
		private final Object current ;
		
		public InspectionDeltaUnit( String id, Object correct, Object previous, Object current ) {
			super();
			this.id = id ;
			this.correct = correct;
			this.previous = previous;
			this.current = current;
		}

		public String getId() {
			return id;
		}

		public Object getPrevious() {
			return previous;
		}

		public Object getCorrect() {
			return correct;
		}

		public Object getCurrent() {
			return current;
		}
	}
	public MediaWikiUniverse(String path, String category) {
		super(path);
		this.pcategory = category;
	}

	IRealm<IThing> realm;
	private Realm<Object> prealm;

	@Override
	public IRealm<IThing> all() {
		if (realm == null)
			realm = super.getRealm(null, pcategory,  null);

		return realm;
	}
	
	@Override
	protected List<String> getDocumentList(){
		ArrayList<String> result = new ArrayList<String>() ;
		for( IThing th : all()  )
			result.add(th.getId()) ;
		
		return result ;
	}

	@Override
	public IRealm<? extends IProperty> allProperties() {
		if (prealm == null) {
			Iterable<IProperty> properties = getProperties(null);
			Realm<Object> realm = new Realm<Object>();
			for (IProperty m : properties) {
				realm.add(m);
			}
			this.prealm = realm;
		}
		return (IRealm<? extends IProperty>) prealm;
	}

	@Override
	public String toString() {
		return pcategory;
	}

	@Override
	public void propertyChanged(String id, String key) {
		if (key.equals(IPropertyConstants.CHAR_PARSER_ID_LABEL)) {
			// TODO FIX ME
			IProperty property = DefaultUniverse.getInstance().getProperty(
					null, id);
			String stringValue = DefaultMetaKeys.getStringValue(property,
					IPropertyConstants.CHAR_PARSER_ID_LABEL);
			if (stringValue != null) {
				AbstractValueParser parser = DefaultParserProvider
						.getInstance().getParser(stringValue);
				if (parser != null
						&& !parser.getClass().getName()
								.contains("DefaultParserProvider")) {
					// NEED SELECTIVE REPARSE
					extractCategory(pcategory);
				}
			}
		}
	}

	public String getRawText(String name) {
		String documentContent = references.getContent( name, 0 ) ;		
		return documentContent;
	}

	@Override
	public void extractImages() {
		PictureExtractor.getInstance().extractPictures(this);
	}
	
	@Override
	protected void doReparse(String id, int flags) {
		ParsedDocument doc = this.getParsedDocument(id) ;
		ParsedDocument oldDocCopy = new ParsedDocument( doc.getName() ) ;
		new ReplacementRequest().operate( oldDocCopy, doc ) ;
		
		super.reparseDocument( id,new ReplacementRequest(),flags);
		detectDelta( oldDocCopy, doc ) ;
	}
	 
	private void detectDelta(ParsedDocument oldDoc, ParsedDocument newDoc) {
		
		WikiThing targetThing = new WikiThing( oldDoc.getName(), this) ;  
		
lab1:	for( ParsedProperty oldProp : oldDoc.getProps().values() )
		{
			String id = oldProp.getName() ;
			
			ParsedProperty newProp = newDoc.getProperty(id) ;
			LinkedHashSet<ParsedValue> oldValues = oldProp.getValues();
			IProperty p = previousValueStorage.getProperty(null, id) ;
			
			if( oldValues == null && newProp != null )
			{
				LinkedHashSet<ParsedValue> newValues = newProp.getValues() ;
				if( newValues != null && newValues.size() != 0 )
				{					
					PropertyAccess.setValue( p, targetThing, oldValues ) ;
					continue ;					
				}
			}			
			
			if( newProp == null || newProp.getValues() == null )
			{				
				PropertyAccess.setValue( p, targetThing, oldValues ) ;
				continue ;
			}
			
			LinkedHashSet<ParsedValue> newValues = newProp.getValues() ;
			
			if( newValues.size() != oldValues.size() )
			{
				
				PropertyAccess.setValue( p, targetThing, oldValues ) ;
				continue ;								
			}			
			
			for( ParsedValue pv: oldValues )
			{
				if( !newValues.contains(pv) )
				{					
					PropertyAccess.setValue( p, targetThing, oldValues ) ;
					continue lab1 ;
				}
			}			
						
		}
		
	}
	/* (non-Javadoc)
	 * @see com.onpositive.mediawiki.dataminer.IHasVersions#contributeStandard(com.onpositive.knowledge.model.IThing, com.onpositive.semantic.model.api.property.IProperty)
	 */
	@Override
	public void contributeStandard( IThing thing, IProperty prop )
	{
		if( prop != null )
		{
			String propId = prop.getId() ;
			Object value = prop.getValue(thing) ;
			IProperty userProp = standardStorage.getProperty( null, propId) ;			
			PropertyAccess.setValue(userProp, thing, value) ;
		}
	}

	@Override
	protected IThing createThing( Object name ) {
		if( !(name instanceof String) )
			return null ;
		
		return new WikiThing( (String)name, this );
	}

	
	@Override
	protected IProperty getSpecialProperty(String propertyName) {
		
		if( propertyName.equals(DocumentIdProperty.PROPERTY_NAME) )
			return new DocumentIdProperty() ;
		
		if( propertyName.equals( IPropertyConstants.ACTUALITY_PROPERTY_METADATA_ID) )
			return new ActualityIndicationProperty( this ) ;
		
		if( propertyName.equals(ExternalDefaultPictureProperty.EXTERNAL_DEFAULT_PICTURE_PROPERTY_NAME) )
			return new ExternalDefaultPictureProperty( this ) ;
		
		if( propertyName.equals(DefaultPictureProperty.DEFAULT_PICTURE_PROPERTY_NAME) )
			return new DefaultPictureProperty( this ) ;
		
		if( propertyName.equals(IPropertyConstants.PARENT_CATEGORIES_PROPERTY_NAME) )
			return new ParentCategoriesProperty(this) ;
		
		return null ;
	}

	@Override
	protected IProperty createProperty(PropertyCharacteristic pChar) {

		return new WikiStorageProperty(pChar, this) ;
	}

	@Override
	protected void addSpecialProperties(Collection<IProperty> col) {

		col.add( new DocumentIdProperty() ) ;
		col.add( new ActualityIndicationProperty( this ) ) ;
		col.add( new ExternalDefaultPictureProperty( this ) ) ;
		col.add( new DefaultPictureProperty( this ) ) ;
		col.add( new ParentCategoriesProperty( this ) ) ;
				
	}
	
	@Override
	public ArrayList<InspectionDeltaUnit> getDelta( IProperty prop )
	{		
		ArrayList<InspectionDeltaUnit> result = new ArrayList<InspectionDeltaUnit>() ;
		for( IThing th : all() )
		{
			InspectionDeltaUnit delta = getDelta(th, prop) ;
			if( delta != null)
				result.add( delta ) ;
		}		
		return result ;
	}

	@Override
	public InspectionDeltaUnit getDelta(IThing thing, IProperty prop) {
		
		String propId = prop.getId() ;
		
		
		Object prevVal = previousValueStorage.getValue(thing, propId) ;
		if( prevVal == null )
			return null ;
		
		final String objId = thing.getId();
		Object currentVal = this.getParsedDocument( objId ).getProperty(propId).getValues() ;
		Object standardValue = standardStorage.getValue(thing, propId) ;
		
		return new InspectionDeltaUnit( objId, standardValue, prevVal, currentVal);
	}
	

}
